﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NetFrame.Log;

namespace NetFrame
{
    public class ServerStart
    {
        Socket mServer;//服务器Socket监听对象
        int mMaxClient;//最大客户端连接数
        /// <summary>
        /// 连接信号量
        /// </summary>
        Semaphore mAcceptClients;
        UserTokenPool mPool;
        private int mClientIndex = 0;
        public LengthEncode LE;
        public LengthDecode LD;
        public Encode Encode;
        public Decode Decode;
        /// <summary>
        /// 消息处理中心，由外部应用传入
        /// </summary>
        public AbsHandlerCenter mCenter;
        /// <summary>
        /// 初始化通信监听
        /// </summary>
        /// <param name="port"></param>
        public ServerStart(int max)
        {
            mServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            mMaxClient = max;
        }

        public void Start(int port)
        {
            Logger.Info("开启服务器");
            //创建连接池
            mPool = new UserTokenPool(mMaxClient);
            mAcceptClients = new Semaphore(mMaxClient, mMaxClient);
            for (int i = 0; i < mMaxClient; i++)
            {
                UserToken token = new UserToken();
                //初始化token信息
                token.mReceiveSAEA.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Comleted);
                token.mSendSAEA.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Comleted);
                token.mLD = LD;
                token.mLE = LE;
                token.mEncode = Encode;
                token.mDecode = Decode;
                token.mSendProcess = ProcessSend;
                token.mClosePross = ClientClose;
                token.mCenter = mCenter;
                mPool.Push(token);
            }
            try
            {
                //监听当前服务器网课所有可用IP地址
                mServer.Bind(new IPEndPoint(IPAddress.Any, port));
                mServer.Listen(10);//连接满了之后最多能连接10个
                StartAccept(null);
            }
            catch (Exception e)
            {
                Logger.Error(e.Message);
            }

        }
        /// <summary>
        /// 开始客户端连接监听
        /// </summary>
        public void StartAccept(SocketAsyncEventArgs e)
        {
            //如果当前传入为null ，说明调用新的客户端连接监听事件，否则移除当前客户端连接
            if (e == null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += new EventHandler<SocketAsyncEventArgs>(Accept_Comleted);
            }
            else
            {
                e.AcceptSocket = null;
            }
            //信号量-1
            mAcceptClients.WaitOne();
            bool result = mServer.AcceptAsync(e);
            //判断异步事件是否挂起  没挂起说明立刻执行完成  直接处理事件  否则会在处理完成后触发Accept_Comleted事件
            if (!result)
            {
                ProcessAccept(e);
            }
        }
        /// <summary>
        /// 处理连接事件
        /// </summary>
        /// <param name="e"></param>
        public void ProcessAccept(SocketAsyncEventArgs e)
        {
          
            //从连接对象池取出连接对象，供新用户使用
            UserToken token = mPool.Pop();
            //Logger.Warn("当前客户端数量："+mClientIndex);
            token.mConn = e.AcceptSocket;
            // TODO 通知应用层 有客户端连接
            mCenter.ClientConnect(token);
            //开启消息到达监听
            StartReceive(token);
            //释放当前异步对象
            StartAccept(e);
        }
        /// <summary>
        /// 连接的回调处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void Accept_Comleted(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }
        /// <summary>
        /// 接收的回调处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void IO_Comleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.LastOperation == SocketAsyncOperation.Receive)
            {
                ProcessReceive(e);
            }
            else
            {
                ProcessSend(e);
            }

        }

        public void ProcessReceive(SocketAsyncEventArgs e)
        {
            UserToken token = e.UserToken as UserToken;
            //判断网络消息接收是否成功
            if (token.mReceiveSAEA.BytesTransferred > 0 && token.mReceiveSAEA.SocketError == SocketError.Success)
            {
                //将接收到的数据拷贝到新建的数组中
                byte[] message = new byte[token.mReceiveSAEA.BytesTransferred];
                Buffer.BlockCopy(token.mReceiveSAEA.Buffer, 0, message, 0, token.mReceiveSAEA.BytesTransferred);
                //处理接收到的消息
                token.Receive(message);
                StartReceive(token);
            }
            else
            {
                if (token.mReceiveSAEA.SocketError != SocketError.Success)
                {
                    ClientClose(token, token.mReceiveSAEA.SocketError.ToString());
                }
                else
                {
                    ClientClose(token, "客户端主动断开连接");
                }
            }
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="e"></param>
        public void ProcessSend(SocketAsyncEventArgs e)
        {
            UserToken token = e.UserToken as UserToken;
            if (e.SocketError != SocketError.Success)
            {
                ClientClose(token, e.SocketError.ToString());
            }
            else
            {
                //消息发送成功
                token.Writed();
            }
        }
        public void StartReceive(UserToken token)
        {

            //用户连接对象，开启异步数据接收
            bool result = token.mConn.ReceiveAsync(token.mReceiveSAEA);
            //异步事件是否挂起
            if (!result)
            {
                ProcessReceive(token.mReceiveSAEA);
            }


        }
        /// <summary>
        /// 客户端断开
        /// </summary>
        /// <param name="token">用户对象</param>
        /// <param name="errorMsg">错误编码</param>
        public void ClientClose(UserToken token, string errorMsg)
        {
            if (token.mConn != null)
            {
                lock (token)
                {
                    //通知应用层，客户端断开连接
                    mCenter.ClientClose(token, errorMsg);
                    token.Close();
                    mPool.Push(token);
                    mAcceptClients.Release();//加一个信号量，让其他用户使用
                }
            }
        }
    }
}
